#ifndef _ASTEROIDSMSG_H_
#define _ASTEROIDSMSG_H_

#include <string>
#include <list>
#include <map>
#include <memory>

class EntityData
{
public:
	EntityData(int id, double x, double y) 
	{ 
		this->id = id; this->x = x; this->y = y; rotation = 0; acelerated = 0;
		//optionals for new asteroids
		this->height = 0; this->width = 0; this->type = 0; this->direction = 0; this->velocity = 0;
	}
	EntityData(const EntityData &e)        
	{  
		this->id = e.id; 
		this->x = e.x; 
		this->y = e.y; 
		this->rotation = e.rotation; 
		this->acelerated = e.acelerated;
		//optionals for new asteroids
		this->direction = e.direction;
		this->height = e.height;
		this->type = e.type;
		this->velocity = e.velocity;
		this->width = e.width;
	}
	EntityData(void)
	{ 
		id = 0; x = 0; y = 0; rotation = 0; acelerated = 0;
		//optionals for new asteroids
		height = 0; width = 0; type = 0; direction = 0; velocity = 0;
	}

	int id, height, width, type;
	double x, y, rotation, direction, velocity;
	int acelerated;
};

class ShipData : public EntityData
{
public:
	ShipData(int id, double x, double y) : EntityData(id, x, y) {}
	ShipData(const EntityData& ed) 	{ id = ed.id; x = ed.x; y = ed.y; rotation = ed.rotation; acelerated = ed.acelerated; isSuperShield = false; 
									  isInvisibilityEnabled = false; isDeadEnabled = false; isShield = false; isBombEnabled = false; isLaserActivated = false;
									  numberOfLifes = 0; shipLife = 0; numberMisiles = 0; shieldLife = 0; baseLife = 0;}
	int numberOfLifes, shipLife, numberMisiles, shieldLife, baseLife;
	bool isSuperShield, isInvisibilityEnabled, isDeadEnabled, isShield, isBombEnabled, isLaserActivated;
};

class BulletData : public EntityData
{
public:
	BulletData(void) {id = 0; x = 0; y = 0; };
	BulletData(int id, double x, double y) : EntityData(id, x, y) {}
	BulletData(const EntityData& ed) { id = ed.id; x = ed.x; y = ed.y; }

	int type;
};

class FileData
{
public:
	FileData() {}
	FileData(const FileData &fd)
	{
		filename = fd.filename;
		len = fd.len;
		bytes.reset(new char[len]);
		memcpy(bytes.get(), fd.bytes.get(), len);
	}

	std::string filename;
	std::auto_ptr<char> bytes;
	int len;
};

class AsteroidsMsg
{
public:
	typedef enum type { LOGIN_MSG, SCENE_DATA_MSG, START_MSG, UPDATE_MSG, CLIENT_UPDATE_MSG};

	AsteroidsMsg(int type);
	AsteroidsMsg() {}
	~AsteroidsMsg(void);

	// getters
	char* getData();
	int   getLen ();

	static std::string getControlCharacter() { return ";";}

	int         getType     () { return type;}
	std::string getLoginMsg () { return loginMsg;}
	std::string getStartMsg () { return startMsg;}

	std::string getSceneFile() { return sceneFile;}

	std::list<FileData>   getFilesData    () { return filesData;}
	std::map<int, EntityData> getAsteroidsData() { return asteroidsData;}
	std::list<ShipData> getShipsData() { return shipsData;}
	std::map<int, BulletData> getBulletsData() { return bulletsData;}
	std::map<int, EntityData> getBonusData() { return bonusData;}

	int getClientId   () { return clientId;}
	int getClientEvent() { return clientEvent;}

	// setters
	void setData(char* data, int len);
	void setData(std::string data);

	void setType    (int type)        { this->type = type;}
	void setLoginMsg(std::string msg) { this->loginMsg = msg;}
	void setStartMsg(std::string msg) { this->startMsg = msg;}

	void addSceneFile       (std::string sceneFile) { this->sceneFile = sceneFile;}

	void addFileData        (std::string name, char* bytes, unsigned long long len);
	void addAsteroidPosition(int asteroidId, double x, double y, double rotation, int acelerated, int height, int width, int type, double direction, double velocity);
	void addShipPosition(int shipId, double x, double y, double rotation, int acelerated, bool isSuperShield, bool isInvisibilityEnabled, bool isDeadEnabled, bool isShield, bool isBombEnabled, bool isLaserActivated, int numberOfLifes, int shipLife, int numberMisiles, int shieldLife, int baseLife);
	void addBulletPosition(int bulletId, double x, double y, double rotation, int type);
	void addBonusPosition(int bonusId, double x, double y, int type);

	void reset();

	void setClientEvent(int type);
	void setClientId   (int id);

	bool isStopAcceleration     () { return stopAccelerate;}
	bool isStopRotation         () { return stopRotation;}

	bool isNewBullet            () { return bulletCreated;}
	bool isNewMissile            () { return missileCreated;}
	bool isNewBomb            () { return bombCreated;}
	bool isShipAccelerating     () { return accelerating;}
	bool isShipAcceleratingLeft () { return acceleratingLeft;}
	bool isShipAcceleratingRight() { return acceleratingRight;}
	bool isShipRotatingLeft     () { return rotatingLeft;}	
	bool isShipRotatingRight    () { return rotatingRight;}
	bool hasClientQuit          () { return quit;}
	std::string toStringWithEnding(void);
private:
	std::list<EntityData> getEntitiesFromUpdateMsg(std::string data, std::size_t begin, std::size_t end);
	std::list<BulletData> getBulletEntitiesFromUpdateMsg(std::string data, std::size_t begin, std::size_t end);
	std::list<ShipData> getShipEntitiesFromUpdateMsg(std::string data, std::size_t begin, std::size_t end);
	std::list<EntityData> getBonusEntitiesFromUpdateMsg(std::string data, std::size_t begin, std::size_t end);
	std::list<FileData> filesData;
	std::list<ShipData> shipsData;
	std::map<int, BulletData> bulletsData;
	std::map<int, EntityData> bonusData;
	std::map<int, EntityData> asteroidsData;
	std::string loginMsg, startMsg, sceneFile, updateMsg;
	int type, clientId, clientEvent;
	bool bulletCreated,bombExploted, missileCreated, bombCreated, accelerating, acceleratingLeft, acceleratingRight, rotatingLeft, rotatingRight, quit, stopAccelerate, stopRotation;
};

#endif
